<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<title>4.2 寸电子墨水屏蓝牙控制器</title>
	<style type="text/css">
		.main { width: 950px; margin: 0 auto; background: #fff; font-size: 1rem; font-weight: 400; line-height: 1.5;}
		h3 { padding-bottom: .3em; border-bottom: 1px solid #CCC; text-align: center; }
		fieldset { border: none; box-shadow: 0 .5rem 0.5rem rgba(0, 0, 0, 0.2); background-color: #f8f9fa; padding: 10px; margin-bottom: 10px; }
		fieldset legend { font-weight: bold; color: rgba(0, 0, 255, 0.6); }
		code { padding: .2em .4em; margin: 0; font-size: 85%; background: #CCC; border-radius: 3px; }

		#status { margin: 10px 0; }
		#log { width: 500px; height: 300px; margin: 0; padding: 5px; background: #DDD; overflow: auto; font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; }
		#log .time { color: #333; }
		#log .action {color: #666; }
		#canvas-box { margin-top: 10px; }
		
		button { padding: 0.375rem 0.75rem; border: 1px solid #0d6efd; border-radius: 0.375rem; }
		button:disabled { opacity: 0.65; }
		button.primary { color: #fff; background-color: #0d6efd; }
		button.primary:hover { color: #fff; border-color: #0b5ed7; background-color: #0b5ed7; }
		button.secondary { color: #fff; background-color: #6c757d; }
		button.secondary:hover { color: #fff; border-color: #565e64; background-color: #5c636a; }

		input[type=text],input[type=number],select { font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; border: 1px solid #dee2e6; border-radius: 0.375rem; padding: .2rem .75rem; }
		input[type=file] { font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; }
		input::file-selector-button { font-size: 1rem; font-weight: 400; line-height: 1.5; border: 1px solid #0d6efd; border-radius: 0.375rem; }
		select { padding: .3rem 2.25rem .3rem .75rem; }
		input:focus,select:focus { border: 1px solid #86b7fe; box-shadow: 0 0 4px rgba(0, 120, 215, 0.8);; outline: 0; }
	</style>
</head>

<body>
	<div class="main">
		<h3>4.2 寸电子墨水屏蓝牙控制器（nRF51）</h3>
		<fieldset>
			<legend>蓝牙连接</legend>
			<div style="display: flex; justify-content: space-between;">
				<div>
					<button id="connectbutton" type="button" class="primary" onclick="preConnect()">连接</button>
					<button id="reconnectbutton" type="button" class="secondary" onclick="reConnect()">重连</button>
				</div>
				<div>
					<label for="epddriver">驱动</label>
					<select id="epddriver" onchange="filterDitheringOptions()">
						<option value="01">EPD_4in2</option>
						<option value="02">EPD_4in2_V2</option>
						<option value="03">EPD_4in2b_V2</option>
					</select>
					<label for="epdpins">引脚</label>
					<input id="epdpins" type="text" value="">
					<button id="setDriverbutton" type="button" class="primary" onclick="setDriver()">确认</button>
				</div>
			</div>
		</fieldset>

		<fieldset>
			<legend>屏幕控制</legend>
			<div style="margin-bottom: 10px; display: flex; justify-content: space-between;">
				<div>
					<button id="clearcanvasbutton" type="button" class="secondary" onclick="clear_canvas()">清除画布</button>
					<button id="sendimgbutton" type="button" class="primary" onclick="sendimg()">发送图片</button>
					<label for="interleavedcount" style="margin-left: 20px;">确认间隔</label>
					<input type="number" id="interleavedcount" value="50" min="0" max="500">
				</div>
				<div>
					<button id="synctimebutton" type="button" class="primary" onclick="syncTime()">日历模式</button>
				</div>
				<div>
					<button id="clearscreenbutton" type="button" class="secondary" onclick="clearScreen()">清除屏幕</button>
					<input type="text" id="cmdTXT" value="">
					<button id="sendcmdbutton" type="button" class="primary" onclick="sendcmd()">发送命令</button>
				</div>
			</div>
			<div style="font-size: 85%; color: #666; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px dotted #AAA;"><b>状态：</b><span id="status"></span></div>
			<div id="sendimg">
				<div style="margin-bottom: 10px; display: flex; justify-content: space-between;">
					<div>
						<lable for="image_file"></lable>
						<input type="file" id="image_file" onchange="update_image()" accept=".png,.jpg,.bmp,.webp,.jpeg">
						<label for="dithering">取模算法</label>
						<select id="dithering" title="取模算法" onchange="update_image()">
							<optgroup data-driver="01|02" label="黑白">
								<option value="none">二值化</option>
								<option value="bayer">bayer</option>
								<option value="floydsteinberg">floydsteinberg</option>
								<option value="Atkinson">Atkinson</option>
							</optgroup>
							<optgroup id="dithering-bwr" data-driver="03" label="黑白红多色">
								<option value="bwr_floydsteinberg">黑白红floydsteinberg</option>
								<option value="bwr_Atkinson">黑白红Atkinson</option>
							</optgroup>
							<optgroup id="dithering-gray" data-driver="01" label="灰色">
								<option value="4gray">4 级灰度</option>
							</optgroup>
						</select>
						<label for="threshold">阈值</label>
						<input type="number" max="255" min="0" value="125" id="threshold" onchange="update_image()">
					</div>
					<button type="button" class="secondary" onclick="clearLog()">清空日志</button>
				</div>
				<div style="display: flex; justify-content: space-between;">
					<canvas id="canvas" width="400" height="300" style="border: black solid 1px;"></canvas>
					<div id="log"></div>
				</div>
			</div>
		</fieldset>
		<fieldset>
			<legend>提示</legend>
			<ul>
				<li><b>驱动选择：</b>黑白屏可尝试 EPD_4in2 / EPD_4in2_V2, 三色屏选择 EPD_4in2b_V2 (选错驱动可能会导致任何未知的异常，重启即可恢复）</li>
				<li><b>引脚配置：</b>格式为十六进制，顺序：MOSI/SCLK/CS/DC/ST/BUSY/BS，必须按此顺序包含完整的 7 个引脚配置（没有用到的引脚可配置为 <code>FF</code>）</li>
				<li><b>确认间隔: </b>这个间隔指的是数据包数量间隔，即发送此数量的不确认响应的数据包后才发送一次需确认响应的数据包。加大此值可优化传图速度，但是丢包风险也更大（你可能会发现图片有部分位置显示不正常，此时需调小这个值）。
				<li><b>日历模式: </b>点击“日历模式”按钮将自动从浏览器同步时间到墨水屏，并切换到日历显示。</li>
				<li>
					<b>指令列表（指令和参数全部要使用十六进制）：</b>
					<ul>
						<li>驱动相关：
							<ul>
								<li><code>00</code>+<code>引脚配置</code>: 设置引脚映射（见上面引脚配置）</li>
								<li><code>01</code>+<code>驱动 ID</code>: 驱动初始化（支持的驱动 ID: <code>01</code>/<code>02</code>/<code>03</code>）</li>
								<li><code>02</code>: 清空屏幕（把屏幕刷为白色）</li>
								<li><code>03</code>+<code>命令</code>: 发送命令到屏幕（请参考屏幕主控手册）</li>
								<li><code>04</code>+<code>数据</code>: 写入数据到屏幕内存（同上）</li>
								<li><code>05</code>: 刷新屏幕（显示已写入屏幕内存的数据）</li>
								<li><code>06</code>: 屏幕睡眠</li>
							</ul>
						</li>
						<li>日历模式：
							<ul>
								<li><code>20</code>+<code>UNIX 时间戳</code>: 同步时间并开启日历模式</li>
							</ul>
						<li>系统相关：
							<ul>
								<li><code>90</code>+<code>配置</code>: 写入配置信息（重启生效，格式参考源码 <code>epd_config_t</code>）</li>
								<li><code>91</code>: 系统重启</li>
								<li><code>92</code>: 系统睡眠</li>
								<li><code>99</code>: 恢复默认设置并重启</li>
							</ul>
						</li>
					</ul>
				</li>
				<li><b>灰度：</b>目前仅 <code>EPD_4in2</code> 驱动支持 4 级灰度，其它驱动选择此选项结果未知。</li>
				<li><b>开源地址：</b>
					<a href="https://github.com/tsl0922/EPD-nRF51">tsl0922/EPD-nRF51</a>,
					交流群：<a href="https://qm.qq.com/q/WEBAZgyyc2">1033086563</a>
				</li>
			</ul>
			<p>
				系统睡眠后可通过线圈（NFC/无线充电器）唤醒（需正确配置线圈对应的引脚才有效），否则一旦系统睡眠只有重新上电才能开启蓝牙。如果价签上带有 LED，系统启动时 LED 会闪一下（需正确配置 LED 对应的引脚才有效），以便知道系统是否已经被线圈唤醒。
			</p>
			<p style="color: #666;">
				<strong>致谢：</strong>屏幕驱动代码来自微雪 <a href="https://www.waveshare.net/wiki/E-Paper_Shield" target="_blank">E-Paper Shield</a>，本网页代码最初基于 <a href="https://github.com/atc1441/ATC_TLSR_Paper" target="_blank">atc1441/ATC_TLSR_Paper</a> 项目的网页控制端代码修改而来。
			</p>
		</fieldset>
	</div>
	<img id="demo-img" style="display: none;" src="" />
	<script type="text/javascript" src="js/dithering.js?v=20250216"></script>
	<script type="text/javascript" src="js/main.js?v=20250216"></script>
</body>

</html>